#!/bin/bash

# A simple script to checkout or update a svn or git repo as source service
#
# (C) 2010 by Adrian Schröter <adrian@suse.de>
#  
# This program is free software; you can redistribute it and/or  
# modify it under the terms of the GNU General Public License  
# as published by the Free Software Foundation; either version 2  
# of the License, or (at your option) any later version.  
# See http://www.gnu.org/licenses/gpl-2.0.html for full license text.  

SERVICE='tar_scm'

set_default_params () {
  MYSCM=""
  MYURL=""
  MYVERSION="_auto_"
  MYFORMAT=""
  MYPREFIX=""
  MYFILENAME=""
  MYREVISION=""
  MYPACKAGEMETA=""
#  MYHISTORYDEPTH=""
  INCLUDES=""
}

get_config_options () {
  # config options for this host ?
  if [ -f /etc/obs/services/$SERVICE ]; then
    . /etc/obs/services/$SERVICE
  fi
  # config options for this user ?
  if [ -f "$HOME"/.obs/$SERVICE ]; then
    . "$HOME"/.obs/$SERVICE
  fi
}

parse_params () {
  while test $# -gt 0; do
    case $1 in
      *-scm)
        MYSCM="$2"
        shift
      ;;
      *-url)
        MYURL="$2"
        CI_PRO_NAME=${MYURL%%/*}
        TEMP_URL="$MYURL"
        MYURL=$TEMP_URL
        shift
      ;;
      *-subdir)
        MYSUBDIR="$2"
        shift
      ;;
      *-revision)
        MYREVISION="$2"
        shift
      ;;
      *-version)
        MYVERSION="$2"
        shift
      ;;
      *-include)
        INCLUDES="$INCLUDES $2"
        shift
      ;;
      *-versionformat)
        MYFORMAT="$2"
        shift
      ;;
      *-versionprefix)
        MYPREFIX="$2"
        shift
      ;;
      *-exclude)
        EXCLUDES="$EXCLUDES --exclude=${2#/}"
        shift
      ;;
      *-filename)
        MYFILENAME="${2#/}"
        shift
      ;;
      *-package-meta)
        MYPACKAGEMETA="${2#/}"
        shift
      ;;
      *-outdir)
        MYOUTDIR="$2"
        shift
      ;;
      *-history-depth)
        echo "history-depth parameter is obsolete and will be ignored"
        shift
      ;;
      *-project)
        MYPROJECT="$2"
        shift
      ;;
      *-package)
        MYPACKAGE="$2"
        shift
      ;;
      *)
        echo "Unknown parameter: $1"
        echo 'Usage: $SERVICE --scm $SCM --url $URL [--subdir $SUBDIR] [--revision $REVISION] [--version $VERSION] [--include $INCLUDE]* [--exclude $EXCLUDE]* [--versionformat $FORMAT] [--versionprefix $PREFIX] [--filename $FILENAME] [--package-meta $META] --outdir $OUT'
        exit 1
      ;;
    esac
    shift
  done
}

error () {
  echo "ERROR: $*"
  exit 1
}

debug () {
  [ -n "$DEBUG_TAR_SCM" ] && echo "$*"
}

safe_run () {
  if ! "$@"; then
    error "$* failed; aborting!"
  fi
}

sanitise_params () {
  TAR_VERSION="$MYVERSION"

  if [ -z "$MYSCM" ]; then
    error "no scm is given via --scm parameter (git/svn/hg/bzr)!"
  fi
  if [ -z "$MYURL" ]; then
    error "no checkout URL is given via --url parameter!"
  fi
  if [ -z "$MYOUTDIR" ]; then
    error "no output directory is given via --outdir parameter!"
  fi
  if [ -z "$MYPROJECT" ]; then
    error "no project is given via --project parameter!"
  fi
  if [ -z "$MYPACKAGE" ]; then
    error "no package is given via --package parameter!"
  fi

  FILE="$MYFILENAME"
  WD_VERSION="$MYVERSION"
  if [ -z "$MYPACKAGEMETA" ]; then
    EXCLUDES="$EXCLUDES --exclude=.svn"
  fi
  # if [ "$MYHISTORYDEPTH" == "full" ]; then
  #   MYHISTORYDEPTH="999999999"
  # fi
}

detect_default_filename_param () {
  if [ -n "$FILE" ]; then
    return
  fi

  case "$MYSCM" in
    git)
      FILE="${MYURL%/}"
      FILE="${FILE##*/}"
      FILE="${FILE%.git}"
      FILE="${FILE#*@*:}"
      ;;
    svn|hg|bzr)
      FILE="${MYURL%/}"
      FILE="${FILE##*/}"
      ;;
    local)
      FILE="temp_dir"
      ;;
    *)
      error "unknown SCM '$MYSCM'"
  esac
}

fetch_upstream () {
  TOHASH="$MYURL"
  [ "$MYSCM" = 'svn' ] && TOHASH="$TOHASH/$MYSUBDIR"
  HASH=`echo "$TOHASH" | sha256sum | cut -d\  -f 1`
  REPOCACHE=
  CACHEDIRECTORY=/tmp/local_code/xdf
  if [ -n "$CACHEDIRECTORY" ]; then
    REPOCACHEINCOMING="$CACHEDIRECTORY/incoming"
    REPOCACHEROOT="$CACHEDIRECTORY/repo"
    REPOCACHE="$REPOCACHEROOT/$MYPROJECT/$MYPACKAGE"
    REPOURLCACHE="$CACHEDIRECTORY/repourl/$HASH"
  fi
  

  debug "check local cache if configured"
  if [ -n "$CACHEDIRECTORY" -a -d "$REPOCACHE/" ]; then
    debug "cache hit: $REPOCACHE"
    check_cache
  else
    if [ -n "$CACHEDIRECTORY" ]; then
      debug "cache miss: $REPOCACHE/"
    else
      debug "cache not enabled"
    fi

    calc_dir_to_clone_to
    debug "new $MYSCM checkout to $CLONE_TO"
    initial_clone

    if [ -n "$CACHEDIRECTORY" ]; then
      #cache_repo
      REPOPATH="$REPOCACHE"
    else
      REPOPATH="$MYOUTDIR/$FILE"
    fi

    if [ "$TAR_VERSION" == "_auto_" -o -n "$MYFORMAT" ]; then
      detect_version
    fi
    #exit 22
  fi

}

calc_dir_to_clone_to () {
  if [ -n "$CACHEDIRECTORY" ]; then
    if [ ! -d REPOCACHE ]; then
      mkdir -p "$REPOCACHE"
    fi
    safe_run cd "$REPOCACHE"
    # Use dry-run mode because git/hg refuse to clone into
    # an empty directory on SLES11
    #debug mktemp -u -d "tmp.XXXXXXXXXX"
    #CLONE_TO=`mktemp -u -d "tmp.XXXXXXXXXX"`
    CLONE_TO="$REPOCACHE"
  else
    CLONE_TO="$FILE"
  fi
}

initial_clone () {
  echo "Fetching from $MYURL ..."

  case "$MYSCM" in
    git)
      # Clone with full depth; so that the revision can be found if specified
      safe_run git clone "$MYURL" "$CLONE_TO"
      ;;
    svn)
      args=
      [ -n "$MYREVISION" ] && args="-r$MYREVISION"
      if [[ $(svn --version --quiet) > "1.5.99" ]]; then
        TRUST_SERVER_CERT="--trust-server-cert"
      fi
      safe_run svn checkout --non-interactive $TRUST_SERVER_CERT \
        $args "$MYURL/$MYSUBDIR" "$CLONE_TO"
      MYSUBDIR= # repo root is subdir
      ;;
    local)
      echo "xdffff: $MYURL ---- $CLONE_TO --- `pwd`"
      safe_run ls -A $MYURL | grep -v .git | xargs -I {} cp -a $MYURL/{} .
      if [ -e $MYURL/.git ]; then
          safe_run rm -f $MYURL/.git/shallow
          safe_run cp -aL $MYURL/.git .
      fi
      if [ -d "$MYURL/.svn" ]; then
        safe_run cp -av $MYURL/.svn ./
      fi
      ;;
    hg)
      safe_run hg clone "$MYURL" "$CLONE_TO"
      ;;
    bzr)
      args=
      [ -n "$MYREVISION" ] && args="-r $MYREVISION"
      safe_run bzr checkout $args "$MYURL" "$CLONE_TO"
      ;;
    *)
      error "unknown SCM '$MYSCM'"
  esac
}

cache_repo () {
  if [ -e "$REPOCACHE" ]; then
    error "Somebody else beat us to populating the cache for $MYURL ($REPOCACHE)"
  else
    # FIXME: small race window here; do source services need to be thread-safe?
    if [ ! -d $REPOCACHE ]; then
      mkdir -p $REPOCACHE
    fi
    debug mv2 "$CLONE_TO" "$REPOCACHE"
    safe_run mv "$CLONE_TO" "$REPOCACHE"
    echo "$MYURL" > "$REPOURLCACHE"
    echo "Cached $MYURL at $REPOCACHE"
  fi
}

check_cache () {
  if [ -d "$MYURL/.svn" ]; then
    new_version=`LC_ALL=C svn info "$MYURL" | sed -n 's,^Last Changed Rev: \(.*\),\1,p'`
  else
    new_version="new_version"
  fi
  if echo "$MYURL" | grep '/$' &> /dev/null; then
    new_version="new_version"
  fi
  if [ -d "$REPOCACHE/.svn" ]; then
    old_version=`LC_ALL=C svn info "$REPOCACHE" | sed -n 's,^Last Changed Rev: \(.*\),\1,p'`
  else
    old_version="old_version"
  fi
  #echo "xdf: $new_version $old_version"
  #if [ "$new_version" != "$old_version" ]; then
    echo "The code has changed for $MYPROJECT/$MYPACKAGE"
    rm -rf "$REPOCACHE"

    calc_dir_to_clone_to
    debug "new $MYSCM checkout to $CLONE_TO"
    initial_clone

    if [ -n "$CACHEDIRECTORY" ]; then
      #cache_repo
      REPOPATH="$REPOCACHE"
    else
      REPOPATH="$MYOUTDIR/$FILE"
    fi

    safe_run cd "$REPOPATH"
    switch_to_revision
    if [ "$TAR_VERSION" == "_auto_" -o -n "$MYFORMAT" ]; then
      detect_version
    fi
}

update_cache () {
  safe_run cd "$REPOCACHE"

  case "$MYSCM" in
    git)
      safe_run git fetch
      ;;
    svn)
      args=
      [ -n "$MYREVISION" ] && args="-r$MYREVISION"
      safe_run svn update $args > svnupdate_info
      isupdate=`cat svnupdate_info | wc -l`
      if [ $isupdate -eq 1 ]; then
        rm -f svnupdate_info
        echo "There is no code update, so exit 22"
        exit 22
      fi
      MYSUBDIR= # repo root is subdir
      ;;
    hg)
      if ! out=`hg pull`; then
        if [[ "$out" == *'no changes found'* ]]; then
          # Contrary to the docs, hg pull returns exit code 1 when
          # there are no changes to pull, but we don't want to treat
          # this as an error.
          :
        else
          error "hg pull failed; aborting!"
        fi
      fi
      ;;
    bzr)
      args=
      [ -n "$MYREVISION" ] && args="-r$MYREVISION"
      safe_run bzr update $args
      ;;
    *)
      error "unknown SCM '$MYSCM'"
  esac
}

switch_to_revision () {
  case "$MYSCM" in
    git)
      safe_run git checkout "$MYREVISION"
      if git branch | grep -q '^\* (no branch)$'; then
        echo "$MYREVISION does not refer to a branch, not attempting git pull"
      else
        safe_run git pull
      fi
      ;;
    svn|bzr|local)
      : # should have already happened via checkout or update
      ;;
    hg)
      safe_run hg update "$MYREVISION"
      ;;
    # bzr)
    #   safe_run bzr update
    #   if [ -n "$MYREVISION" ]; then
    #     safe_run bzr revert -r "$MYREVISION"
    #   fi
    #   ;;
    *)
      error "unknown SCM '$MYSCM'"
  esac
}

detect_version () {
  if [ -z "$MYFORMAT" ]; then
    case "$MYSCM" in
      git)
        MYFORMAT="%at"
        ;;
      hg)
        MYFORMAT="{rev}"
        ;;
      svn|bzr)
        MYFORMAT="%r"
        ;;
      *)
        error "unknown SCM '$MYSCM'"
        ;;
    esac
  fi

  safe_run cd "$REPOPATH"
  if [ -n "$MYFORMAT" ];then
      MYPREFIX="$MYFORMAT"
  else
      get_version
  fi
  TAR_VERSION="$MYPREFIX$version"
}

get_version () {
  case "$MYSCM" in
    git)
      #version=`safe_run git show --pretty=format:"$MYFORMAT" | head -n 1`
      version=`safe_run git log -n1 --pretty=format:"$MYFORMAT"`
      ;;
    svn)
      #rev=`LC_ALL=C safe_run svn info | awk '/^Revision:/ { print $2 }'`
      rev=`LC_ALL=C safe_run svn info | sed -n 's,^Last Changed Rev: \(.*\),\1,p'`
      version="${MYFORMAT//%r/$rev}"
      ;;
    hg)
      rev=`safe_run hg id -n`
      version=`safe_run hg log -l1 -r$rev --template "$MYFORMAT"`
      ;;
    bzr)
      #safe_run bzr log -l1 ...
      rev=`safe_run bzr revno`
      version="${MYFORMAT//%r/$rev}"
      ;;
    *)
      error "unknown SCM '$MYSCM'"
  esac
}

prep_tree_for_tar () {
  if [ ! -e "$REPOPATH/$MYSUBDIR" ]; then
    error "directory does not exist: $REPOPATH/$MYSUBDIR"
  fi

  if [ -z "$TAR_VERSION" ]; then
    TAR_BASENAME="$FILE"
  else
    TAR_BASENAME="${FILE}-${TAR_VERSION}"
  fi

  MYINCLUDES=""

  for INC in $INCLUDES; do
    MYINCLUDES="$MYINCLUDES $INC"
  done
  #if [ -z "$MYINCLUDES" ]; then
  #  MYINCLUDES="*"
  #fi

  safe_run cd "$MYOUTDIR"

  if [ -n "$CACHEDIRECTORY" ]; then
    debug cp -a "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME"
    safe_run cp -a "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME"
    if [ -e $REPOPATH/$MYSUBDIR/.git ]; then
        # amazing copy failed, ignore fail temporary
        cp -a "$REPOPATH/$MYSUBDIR/.git" "$TAR_BASENAME"
        safe_run pushd "$TAR_BASENAME";git reset --hard HEAD;popd
    fi
  else
    debug mv3 "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME"
    safe_run mv "$REPOPATH/$MYSUBDIR" "$TAR_BASENAME"
  fi
  if [ -z "$MYINCLUDES" ]; then
    MYINCLUDES=`ls -A $TAR_BASENAME`
  fi
}

create_tar () {
  safe_run cd "$TAR_BASENAME"

  compression_array=(`cat $MYOUTDIR/$TARFILE/_service | egrep '"compression"' | awk -F'>' '{print $2}' | awk -F'<' '{print $1}'`)
  file_array=`cat $MYOUTDIR/$TARFILE/_service | egrep '"file"' | awk -F'>' '{print $2}' | awk -F'<' '{print $1}' | tr -d '.tar'`
  index=0
  for file in $file_array
  do
    if echo "$TAR_BASENAME" | egrep "$file"; then
      break
    else
       ((index=index+1))
    fi
  done
  compression_type=${compression_array[index]}
  if [ -e .git ]; then
      MYINCLUDES="$MYINCLUDES .git"
  fi 
  
      TARFILE="${TAR_BASENAME}.tar"
      TARPATH="$MYOUTDIR/$TARFILE"
      debug tar Pcf "$TARPATH" $EXCLUDES $MYINCLUDES
      safe_run tar Pcf "$TARPATH" $EXCLUDES $MYINCLUDES


  echo "Created $TARFILE"
  safe_run cd "$MYOUTDIR"
}

cleanup () {
  debug rm -rf "$TAR_BASENAME" "$FILE"
  rm -rf "$TAR_BASENAME" "$FILE"
}

main () {
  set_default_params
  #xdf
  DEBUG_TAR_SCM=1
  
  if [ -z "$DEBUG_TAR_SCM" ]; then
    get_config_options
  else
    # We're in test-mode, so don't let any local site-wide
    # or per-user config impact the test suite.
    :
  fi
  parse_params "$@"
  sanitise_params

  SRCDIR=$(pwd)
  cd "$MYOUTDIR"
  #echo "$SRCDIR $MYOUTDIR"
  detect_default_filename_param
  
  #xdf
  #LOGFILE=/srv/local_code/xdf/log/$MYPROJECT/$MYPACKAGE
  #mkdir -p "/srv/local_code/xdf/log/$MYPROJECT"

  lockfile=$LOGFILE".lock"
  if [ -f $lockfile ]; then
    mypid=`cat $lockfile`
    while ps -p $mypid -o comm= &> /dev/null
    do
      sleep 10
      mypid=`cat $lockfile`
    done
    rm -f $lockfile
  fi
  touch $lockfile
  echo "$$" > $lockfile
  
  #exec 6>&1
  #exec > $LOGFILE
  echo "$@"
  echo "myurl === $MYURL"
  fetch_upstream

  prep_tree_for_tar
  create_tar

  cleanup
  rm -f $lockfile
}

main "$@"

exit 0
